<template>
	<div ref="outerWrapper" :class="wrapperClasses">
		<div v-if="isHorizontal" :class="`${prefix}-horizontal`">
			<div :style="{ right: `${anotherOffset}%` }" :class="[`${prefix}-pane`, 'left-pane']"><slot name="left" /></div>
			<div :class="`${prefix}-trigger-con`" :style="{ left: `${offset}%` }" @mousedown="handleMousedown">
				<slot name="trigger">
					<trigger mode="vertical" />
				</slot>
			</div>
			<div :style="{ left: `${offset}%` }" :class="[`${prefix}-pane`, 'right-pane']"><slot name="right" /></div>
		</div>
		<div v-else :class="`${prefix}-vertical`">
			<div :style="{ bottom: `${anotherOffset}%` }" :class="[`${prefix}-pane`, 'top-pane']"><slot name="top" /></div>
			<div :class="`${prefix}-trigger-con`" :style="{ top: `${offset}%` }" @mousedown="handleMousedown">
				<slot name="trigger">
					<trigger mode="horizontal" />
				</slot>
			</div>
			<div :style="{ top: `${offset}%` }" :class="[`${prefix}-pane`, 'bottom-pane']"><slot name="bottom" /></div>
		</div>
	</div>
</template>

<script>
import { oneOf, on, off } from '@/libs/tools';
import Trigger from './trigger.vue';
export default {
	name: 'SplitPane',
	components: {
		Trigger
	},
	props: {
		value: {
			type: [Number, String],
			default: 0.5
		},
		mode: {
			validator(value) {
				return oneOf(value, ['horizontal', 'vertical']);
			},
			default: 'horizontal'
		},
		min: {
			type: [Number, String],
			default: '40px'
		},
		max: {
			type: [Number, String],
			default: '40px'
		}
	},
	/**
	 * Events
	 * @on-move-start
	 * @on-moving 返回值：事件对象，但是在事件对象中加入了两个参数：atMin(当前是否在最小值处), atMax(当前是否在最大值处)
	 * @on-move-end
	 */
	data() {
		return {
			prefix: 'ivu-split',
			offset: 0,
			oldOffset: 0,
			isMoving: false
		};
	},
	computed: {
		wrapperClasses() {
			return [`${this.prefix}-wrapper`, this.isMoving ? 'no-select' : ''];
		},
		isHorizontal() {
			return this.mode === 'horizontal';
		},
		anotherOffset() {
			return 100 - this.offset;
		},
		valueIsPx() {
			return typeof this.value === 'string';
		},
		offsetSize() {
			return this.isHorizontal ? 'offsetWidth' : 'offsetHeight';
		},
		computedMin() {
			return this.getComputedThresholdValue('min');
		},
		computedMax() {
			return this.getComputedThresholdValue('max');
		}
	},
	methods: {
		px2percent(numerator, denominator) {
			return parseFloat(numerator) / parseFloat(denominator);
		},
		getComputedThresholdValue(type) {
			let size = this.$refs.outerWrapper[this.offsetSize];
			if (this.valueIsPx) return typeof this[type] === 'string' ? this[type] : size * this[type];
			else return typeof this[type] === 'string' ? this.px2percent(this[type], size) : this[type];
		},
		getMin(value1, value2) {
			if (this.valueIsPx) return `${Math.min(parseFloat(value1), parseFloat(value2))}px`;
			else return Math.min(value1, value2);
		},
		getMax(value1, value2) {
			if (this.valueIsPx) return `${Math.max(parseFloat(value1), parseFloat(value2))}px`;
			else return Math.max(value1, value2);
		},
		getAnotherOffset(value) {
			let res = 0;
			if (this.valueIsPx) res = `${this.$refs.outerWrapper[this.offsetSize] - parseFloat(value)}px`;
			else res = 1 - value;
			return res;
		},
		handleMove(e) {
			let pageOffset = this.isHorizontal ? e.pageX : e.pageY;
			let offset = pageOffset - this.initOffset;
			let outerWidth = this.$refs.outerWrapper[this.offsetSize];
			let value = this.valueIsPx ? `${parseFloat(this.oldOffset) + offset}px` : this.px2percent(outerWidth * this.oldOffset + offset, outerWidth);
			let anotherValue = this.getAnotherOffset(value);
			if (parseFloat(value) <= parseFloat(this.computedMin)) value = this.getMax(value, this.computedMin);
			if (parseFloat(anotherValue) <= parseFloat(this.computedMax)) value = this.getAnotherOffset(this.getMax(anotherValue, this.computedMax));
			e.atMin = this.value === this.computedMin;
			e.atMax = this.valueIsPx
				? this.getAnotherOffset(this.value) === this.computedMax
				: this.getAnotherOffset(this.value).toFixed(5) === this.computedMax.toFixed(5);
			this.$emit('input', value);
			this.$emit('on-moving', e);
		},
		handleUp() {
			this.isMoving = false;
			off(document, 'mousemove', this.handleMove);
			off(document, 'mouseup', this.handleUp);
			this.$emit('on-move-end');
		},
		handleMousedown(e) {
			this.initOffset = this.isHorizontal ? e.pageX : e.pageY;
			this.oldOffset = this.value;
			this.isMoving = true;
			on(document, 'mousemove', this.handleMove);
			on(document, 'mouseup', this.handleUp);
			this.$emit('on-move-start');
		}
	},
	watch: {
		value() {
			this.offset = ((this.valueIsPx ? this.px2percent(this.value, this.$refs.outerWrapper[this.offsetSize]) : this.value) * 10000) / 100;
		}
	},
	mounted() {
		this.$nextTick(() => {
			this.offset = ((this.valueIsPx ? this.px2percent(this.value, this.$refs.outerWrapper[this.offsetSize]) : this.value) * 10000) / 100;
		});
	}
};
</script>

<style lang="less">
@import './index.less';
</style>
